package fr.nts.framework.front.security.provider;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import fr.nts.framework.front.security.service.SimpleUserService;
import fr.nts.framework.front.security.service.exception.BadAuthentificationException;

/**
 * Le provider par défaut pour l'authentification d'une application web avec Spring Security
 * 
 * @author sco
 * @version 1.0.0
 */
@Component
public class BasicSecurityProvider implements AuthenticationProvider{

    protected static final Logger log = LoggerFactory.getLogger(BasicSecurityProvider.class);
    
    /**
     * Le service de base de spring security permettant de générer des objets UserDetails
     */
    @Resource(name = "simpleUserDetailsService")
    protected UserDetailsService userDetailsService;

    @Resource(name = "simpleUserService")
    private SimpleUserService simpleUserService;
    
    /**
     * Encodeur de password, MD5 par defaut
     */
    private PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
    
    /*
     * (non-Javadoc)
     * @see org.springframework.security.authentication.AuthenticationProvider#supports(java.lang.Class)
     */
    @Override
    public boolean supports(Class<? extends Object> authentication){
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

    
    /*
     * (non-Javadoc)
     * @see org.springframework.security.authentication.AuthenticationProvider#authenticate(org.springframework.security.core.Authentication)
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException{
        
        // L'authentification: Test du couple login/mot de passe
        UserDetails user = doInternalAuthenticate(authentication);

        // Le token de base pour l'identification utilisé coté client
        UsernamePasswordAuthenticationToken simpleUserAuthentication = new UsernamePasswordAuthenticationToken(user, 
                                                                                                               null, 
                                                                                                               user.getAuthorities());
        return simpleUserAuthentication;
    }


    /**
     * Le système d'authentification
     */
    protected UserDetails doInternalAuthenticate(Authentication authentication) throws BadCredentialsException{
    	// Le login de l'utilisateur
        String principal = (String)authentication.getPrincipal();
        
        // Le mot de passe de l'utilisateur
        String password = (String)authentication.getCredentials();

        // Tentative d'authentification
        try{
        	simpleUserService.authentification(principal, 
        			                           passwordEncoder.encodePassword(password, null));
        // Mauvais login/password
        }catch(BadAuthentificationException be){
        	log.info("L'utilisateur avec le couple login password: [{}/{}] ne s'est pas authentifié correctement.", principal, password);
            throw new BadCredentialsException("Login/Password incorrect");
        }
        
        // On charge la personne
        return loadUserByUsername(principal);
    }
    
    
    /**
     * Charge l'utilisateur NTS
     */
    protected UserDetails loadUserByUsername(final String username){
    	return userDetailsService.loadUserByUsername(username);
    }


    // GETTER / SETTER
	public void setPasswordEncoder(PasswordEncoder passwordEncoder){
		this.passwordEncoder = passwordEncoder;
	}
}